\documentclass[12pt]{report}
\usepackage{geometry}
\geometry{a4paper,scale=0.8}
\usepackage{longtable}
\usepackage{url}
\usepackage{listings}
\usepackage{graphicx}
\usepackage{float}
\usepackage{titlesec}
\usepackage{CJK}
\begin{CJK}{UTF8}{gkai}
\titleformat{\chapter}[hang]{\centering\Huge\bfseries}{\chaptername}{1em}{}
%\renewcommand{\chaptername}{第\CJKnumber{thechapter}章}
\title{A006 编写linux0.01可以运行的程序}
\author{Chuzy}
\date{2020/05/03}

\lstset{
    basicstyle          =   \small\ttfamily,          % 基本代码风格
    keywordstyle        =   \bfseries,          % 关键字风格
    commentstyle        =   \rmfamily\itshape,  % 注释的风格，斜体
    stringstyle         =   \ttfamily,  % 字符串风格
    flexiblecolumns,                % 别问为什么，加上这个
    numbers             =   left,   % 行号的位置在左边
    showspaces          =   false,  % 是否显示空格，显示了有点乱，所以不现实了
    numberstyle         =   \ttfamily,    % 行号的样式，小五号，tt等宽字体
    showstringspaces    =   false,
    captionpos          =   t,      % 这段代码的名字所呈现的位置，t指的是top上面
    frame               =   lrtb,   % 显示边框
    columns         =   flexible,     
    breaklines      =   true,
    language        =  [ANSI]C,
    escapeinside=``
}


%以上部分叫做"导言区",下面才开始写正文
\begin{document}

%先插入标题
\maketitle
\tableofcontents
\newpage 
\chapter{概述}
在005版本中，我们利用内核代码，编写了一个能方便的把文件从我们正在使用的系统中复制到我们内核使用的硬盘映像文件上的程序，到现在为止，操作系统已经能够从磁盘映像文件上加载自身并开始运行。然而，要作为一个有用的操作系统，我们还需要生成和运行我们自己的程序的能力。
%\begin{figure}[htbp]
%\centering
%\includegraphics[scale=0.5]{fig/36.png}
%\caption{0.004版本运行结果}
%\label{fig:Version 0.004}
%\end{figure}
\chapter{流程图}
include目录为内核对外提供的头文件；lib为内核对外提供的库函数；src为我们自己的代码。
\begin{longtable}{|l|l|p{5cm}|p{3cm}|} 
\caption{0.003版本的文件}  
\label{tab: 0.004 files} \\ 
\hline 目录&文件名&作用&变化 \\
\hline include & stdlib.h & 增加了lib目录下文件的处理 & 新增\\ 
\hline src & main.c & 增加了新的内核文件 & 新增\\
\hline src & Makefile & 增加了新的内核文件 & 新增\\
\hline lib & printf.c & 增加了新的内核文件 & 新增\\
\hline lib & vsprintf.c & 增加了新的内核文件 & 移植\\
\hline lib & write.c & 增加了新的内核文件 & 移植\\
\hline lib & errno.c & 增加了新的内核文件 & 移植\\
\hline lib & Makefile & 增加了新的内核文件 & 新增\\
\hline lib/include & stdarg.h & 增加了新的内核文件 & 移植\\
\hline lib/include & string.h & 增加了新的内核文件 & 移植\\
\hline lib/include & unistd.h & 增加了新的内核文件 & 移植\\
\hline lib/include & utime.h & 增加了新的内核文件 & 移植\\
\hline lib/include/sys & stat.h & 增加了新的内核文件 & 移植\\
\hline lib/include/sys & times.h & 增加了新的内核文件 & 移植\\
\hline lib/include/sys & utsname.h & 增加了新的内核文件 & 移植\\	
\hline tools & build.c & elf32转换成a.out & 新增\\
\hline  
\end{longtable}  
\paragraph*{编译内核提供的库文件} 
我们还记得，在第一部分中，我们介绍了tools/build.c，这个文件的功能是把编译声场的boot和system合并在一起，生成我们需要的磁盘img文件。如果是制作文件系统，我们要做的事情是：
1. 读入要制作文件系统的磁盘映像
2. 读入分区表，定位分区
3. 写入Super Block
\paragraph{编译我们自己的应用代码} 
如果是要把复制文件，我们要做的事情是：
1. 读入的磁盘映像
2. 读入分区表，定位分区
3. 读入Super Block
4. 读入inode
5. 定位目标路径
6. 如果目标路径不存在，创建目标路径
7. 如果目标文件已存在，覆盖目标文件
8. 复制文件内容
对内核的改动，在init函数中，读取并显示指定目录下的文件内容。
\paragraph{把编译好的应用复制到磁盘镜像文件上} 
\paragraph{改写内核运行我们的应用代码} 

\chapter{功能入口源码src/main.c}
\section{main函数}
调用内核提供的printf函数，打印一个Hello World。
\begin{lstlisting}
void main(void)
{
        printf("> ");
        printf("Hello world!\r\n");
        for(;;) pause();
}
\end{lstlisting}
\chapter{主Makefile}
在LD的选项中，我们设置程序的入口点和入口偏移地址，并链接main.o和../lib目录下的lib.o库文件，生成我们的可运行文件m2。
\begin{lstlisting}
AR      =gar
AS      =as --32
LD      =ld -melf_i386 -e main -Ttext 0
LDFLAGS =-s -x -M
CC      =gcc -m32
CFLAGS  =-w -O -fstrength-reduce -fomit-frame-pointer \
        -finline-functions -fno-stack-protector \
        -nostdinc -I../include

.c.s:
        $(CC) $(CFLAGS) \
        -S -o $*.s $<
.s.o:
        $(AS) -c -o $*.o $<
.c.o:
        $(CC) $(CFLAGS) \
        -c -o $*.o $<

LIBS  = ../lib/lib.o
all:    $(LIBS) main.o
        $(LD) $(LDFLAGS) -o m2 main.o $(LIBS) > main.map
\end{lstlisting}
\chapter{库文件lib/printf.c}
内核并未提供用户态的打印输出函数，我们新增一个。
\section{printf函数}
\begin{lstlisting}[breaklines]
#include <unistd.h>
#include <stdarg.h>

static char printbuf[1024];
static int printf(const char *fmt, ...)
{
        va_list args;
        int i;

        va_start(args, fmt);
    	i=vsprintf(printbuf, fmt, args);
        write(1,printbuf,i);
        va_end(args);
        return i;
}
\end{lstlisting}
\chapter{库文件lib/vsprintf.c}
移植，不再赘述
\chapter{库文件lib/write.c}
继承，增加一个pause的中断函数定义，我们的程序需要pause函数。
\begin{lstlisting}[breaklines]
#define __LIBRARY__
#include <unistd.h>

inline _syscall0(int,pause);
_syscall3(int,write,int,fd,const char *,buf,off_t,count)
\end{lstlisting}
\chapter{库文件lib/errno.c}
移植，不再赘述
\chapter{库文件lib/Makefile}
通过Makefile，我们编译生成一个lib.o.
\begin{lstlisting}
AR      =gar
AS      =as --32
LD      =ld -melf_i386
LDFLAGS =-s -x
CC      =gcc -m32
CFLAGS  =-w -O -fstrength-reduce -fomit-frame-pointer \
        -finline-functions -fno-stack-protector \
        -nostdinc -D__LIBRARY__ -Iinclude
CPP     =gcc -E -nostdinc -Iinclude
.c.s:
        $(CC) $(CFLAGS) \
        -S -o $*.s $<
.s.o:
        $(AS) -c -o $*.o $<
.c.o:
        $(CC) $(CFLAGS) \
        -c -o $*.o $<

LIBS  = vsprintf.o write.o errno.o printf.o
lib.o:  $(LIBS)
        $(LD) $(LDFLAGS) -r -o lib.o $(LIBS)
\end{lstlisting}
\chapter{转换工具源码tools/build.c}
linux0.01内核并不认识我们使用ld -melf\_i386 选项生成的执行文件，在使用之前，我们需要将其转换成为符合ZMAGIC的格式。
\begin{lstlisting}
#include <stdio.h>	/* fprintf */
#include <stdlib.h>	/* contains exit */
#include <sys/types.h>	/* unistd.h needs this */
#include <unistd.h>	/* contains read/write */
#include <fcntl.h>
#include <malloc.h>
#include <string.h>
#include </usr/include/elf.h>

struct exec {
  unsigned long a_magic;        /* Use macros N_MAGIC, etc for access */
  unsigned a_text;              /* length of text, in bytes */
  unsigned a_data;              /* length of data, in bytes */
  unsigned a_bss;               /* length of uninitialized data area for file, in bytes */
  unsigned a_syms;              /* length of symbol table data in file, in bytes */     
  unsigned a_entry;             /* start address */
  unsigned a_trsize;            /* length of relocation info for text, in bytes */
  unsigned a_drsize;            /* length of relocation info for data, in bytes */      
};   
#define ZMAGIC 0413


#define MINIX_HEADER 32
#define GCC_HEADER 4096

void die(char * str)
{
	fprintf(stderr,"%s\n",str);
	exit(1);
}

void usage(void)
{
	die("Usage: build input [> output]");
}

int main(int argc, char ** argv)
{
	int i,c,id;
	char buf[4096];
	
	Elf32_Ehdr ElfHead;
	Elf32_Ehdr * pElfHead = &ElfHead;

	int lenShdr;
	Elf32_Shdr * pShdr;

	char * pNameTab=0;
	char * pName;
	
	struct exec ex;
	unsigned long size_text, size_data, addr_text, addr_data;
	char * pWritebuf;

	if (argc != 2)
		usage();
	for (i=0;i<sizeof buf; i++) buf[i]=0;
	if ((id=open(argv[1],O_RDONLY,0))<0)
		die("Unable to open input");
	if (read(id,buf,GCC_HEADER) != GCC_HEADER)
		die("Unable to read input header");
	if (((long *) buf)[6] != 0)
		die("Non-GCC header of input");
	
	ElfHead = *((Elf32_Ehdr * )buf);
	fprintf(stderr, "%s", "ELF Header:\n");
	fprintf(stderr, "%s","Magic:");
	for(i=0; i<EI_NIDENT; i++)
	{
		fprintf(stderr, " 0x%x", pElfHead->e_ident[i]);
	}
	fprintf(stderr, "%s", "\n");
	fprintf(stderr, "Entry Point:\t0x%x\n", pElfHead->e_entry);
	fprintf(stderr, "S Header offet:\t0x%x\n", pElfHead->e_shoff);	
	fprintf(stderr, "S Header size:\t0x%x\n", pElfHead->e_shentsize);
	fprintf(stderr, "S Header num:\t0x%x\n", pElfHead->e_shnum);
	fprintf(stderr, "S Index:\t0x%x\n", pElfHead->e_shstrndx);

	lenShdr = pElfHead->e_shnum * pElfHead->e_shentsize;
	pShdr = (Elf32_Shdr *)malloc(lenShdr);
	if(lseek(id,pElfHead->e_shoff,SEEK_SET) >= 0)
	{
		c = read(id, pShdr, lenShdr);
	}
	else
	{
		die("Read Sesstion header failed!\n");
	}

	for(i=0; i<pElfHead->e_shnum; i++)
        {
		if(((pShdr[i]).sh_type) == SHT_STRTAB)
		{
			pNameTab = (char *)malloc((pShdr[i]).sh_size);
			if(lseek(id,(pShdr[i]).sh_offset,SEEK_SET) >= 0)
		 	{
				c = read(id, pNameTab, (pShdr[i]).sh_size);
			}
			else
			{
				die("Read Sesstion header failed!\n");
			}
			break;
		}
        }

	if(pNameTab == 0)
	{
		die("No SHT_STRTAB found!\n");
	}

	ex.a_magic = ZMAGIC;
	ex.a_trsize = 0;
	ex.a_drsize = 0;
	ex.a_syms = 0;
	ex.a_entry = 0;

	for(i=0; i<pElfHead->e_shnum; i++)
        {
                pName = &(pNameTab[(pShdr[i]).sh_name]);
                fprintf(stderr,"[%d]",i);
                fprintf(stderr,"\t0x%x",(pShdr[i]).sh_type);
                fprintf(stderr,"\t0x%x",(pShdr[i]).sh_offset);
                fprintf(stderr,"\t0x%x",(pShdr[i]).sh_addr);
                fprintf(stderr,"\t0x%x",(pShdr[i]).sh_size);
                fprintf(stderr,"\t%s",pName);
                fprintf(stderr,"%s","\n");

                if(strcmp(pName, ".text")==0)
                {
                        addr_text = (pShdr[i]).sh_offset;
			size_text = (pShdr[i]).sh_size;
			ex.a_text = (size_text + 0xfff) & 0xfffff000;
                        fprintf(stderr,"text addr 0x%x.\n",addr_text);
                        fprintf(stderr,"text size %d.\n",ex.a_text);
		}
		else if(strcmp(pName, ".rodata") == 0)
		{
                        addr_data = (pShdr[i]).sh_offset;
 			size_data = (pShdr[i]).sh_size;			
  			ex.a_data = (size_data + 0xfff) & 0xfffff000;
                        fprintf(stderr,"data addr 0x%x.\n",addr_data);
                        fprintf(stderr,"data size %d.\n",ex.a_data);
		}
		else if(strcmp(pName, ".bss")==0)
		{
  			ex.a_bss = ((pShdr[i]).sh_size + 0xfff) & 0xfffff000;
                        fprintf(stderr,"bss size %d.\n",ex.a_bss);
		}
	}
  
	write(1,(char *)(&ex),1024);
                        
	pWritebuf = (char *)malloc(ex.a_text);
	if(lseek(id,addr_text,SEEK_SET) >= 0)
	{
		if((c=read(id, pWritebuf, size_text)) > 0)
		{
			c = write(1,pWritebuf, ex.a_text);
			fprintf(stderr,"Write text %d bytes.\n",size_text);
		}
	}
	else
	{                               
	       	die("Seek Text Sesstion failed!\n");
	}

        pWritebuf = (char *)realloc(pWritebuf, ex.a_data);
        if(lseek(id,addr_data,SEEK_SET) >= 0)
        {
                if((c=read(id, pWritebuf, size_data)) > 0)
                {
                        c = write(1,pWritebuf, ex.a_data);
                        fprintf(stderr,"Write data  %d bytes.\n",size_data);
                }
        }
        else
        {
                die("Seek Data Sesstion failed!\n");
        }
	
	close(id);
	close(1);
	return(0);
}
\end{lstlisting}
\chapter{转换工具tools/Makefile}
\begin{lstlisting}[breaklines]
#
# Makefile for the FREAX-kernel.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#

AR	=gar
AS	=as --32
LD	=ld -melf_i386 -e main -Ttext 0
LDFLAGS	=-s -x -M
CC	=gcc -m32
CFLAGS	=-w -O -fstrength-reduce -fomit-frame-pointer \
	-finline-functions 

.c.s:
	$(CC) $(CFLAGS) \
	-S -o $*.s $<
.s.o:
	$(AS) -c -o $*.o $<
.c.o:
	$(CC) $(CFLAGS) \
	-c -o $*.o $<

all: build.c
	$(CC) $(CFLAGS) \
        -o build build.c
\end{lstlisting}

\chapter{模拟运行}
现在，我们就可以着手把我们编译好的文件复制到磁盘映像里面了。
\section{编译目标文件}
\begin{lstlisting}[breaklines]
cd lib
make
cd ../src
make
cd ../tools
./build sh > osh
\end{lstlisting}
\section{复制到硬盘映像}
\begin{lstlisting}[breaklines]
./m2 1 d1.img
./m2 3 d1.img osh
\end{lstlisting}


\end{CJK}
\end{document}
